package com.yuyu.commons.config.db;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.logging.Log4j2Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.yuyu.commons.aspect.DataSourceAspect;
import com.yuyu.commons.toolKit.DataUtil;
import com.yuyu.commons.toolKit.PropertiesUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.*;
import org.springframework.core.type.AnnotatedTypeMetadata;

import javax.sql.DataSource;
import java.util.*;

@Configuration
@ConditionalOnClass(DruidDataSource.class)
public class DataSourceConfig {

    public static class EnableAspect implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return DataUtil.isNotEmpty(PropertiesUtil.getString("druid.reader.url"));
        }
    }

    @Bean
    @Conditional(EnableAspect.class)
    public DataSourceAspect providerAspect() {
        return new DataSourceAspect();
    }

    @Bean
    public DataSource dataSource() {
        boolean single = isSingle();
        DataSource write = getDataSource(false);
        HashMap<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("write", write);
        if (!single) {
            DataSource read = getDataSource(true);
            targetDataSources.put("read", read);
        }

        ChooseDataSource dataSource = new ChooseDataSource();
        dataSource.setDefaultTargetDataSource(write);
        dataSource.setTargetDataSources(targetDataSources);
        Map<String, String> method = new HashMap<>();
        method.put("write", "transaction,add,insert,create,update,delete,remove,save,modify,");
        method.put("read", ",get,select,count,list,query,find,do,");
        dataSource.setMethodType(method);
        return dataSource;
    }

    @Bean
    public ServletRegistrationBean<StatViewServlet> druidServlet() {
        ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<>();
        servletRegistrationBean.setServlet(new StatViewServlet());
        servletRegistrationBean.addUrlMappings("/druid/*");
        return servletRegistrationBean;
    }

    /**
     * 配置Druid的监控
     * 配置一个管理后台的Servlet
     *
     * @author hzh
     * @date
     * @param
     * @return org.springframework.boot.web.servlet.ServletRegistrationBean
     */
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        Map<String,String> initParams = new HashMap<>();
        initParams.put("loginUsername","admin");
        initParams.put("loginPassword","123456");
        //默认就是允许所有访问
        initParams.put("allow","");
        initParams.put("deny","");
        bean.setInitParameters(initParams);
        return bean;
    }

    /**
     * 配置一个web监控的filter
     */
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        Map<String,String> initParams = new HashMap<>();
        initParams.put("exclusions","*.js,*.css,/druid/*");
        bean.setInitParameters(initParams);
        bean.setUrlPatterns(Arrays.asList("/*"));
        return  bean;
    }

    private DataSource getDataSource(boolean readOnly) {
        DruidDataSource datasource = new DruidDataSource();
        if (readOnly) {
            datasource.setUrl(PropertiesUtil.getString("druid.reader.url"));
            datasource.setUsername(PropertiesUtil.getString("druid.reader.username"));
            datasource.setPassword(PropertiesUtil.getString("druid.reader.password"));
        } else if (DataUtil.isNotEmpty(PropertiesUtil.getString("druid.writer.url"))
                && DataUtil.isNotEmpty(PropertiesUtil.getString("druid.writer.username"))) {
            datasource.setUrl(PropertiesUtil.getString("druid.writer.url"));
            datasource.setUsername(PropertiesUtil.getString("druid.writer.username"));
            datasource.setPassword(PropertiesUtil.getString("druid.writer.password"));
        }

        datasource.setInitialSize(50);
        datasource.setMinIdle(4);
        datasource.setMaxActive(200);
        datasource.setRemoveAbandoned(false);
        datasource.setRemoveAbandonedTimeout(180);
        datasource.setTimeBetweenEvictionRunsMillis(Long.parseLong(PropertiesUtil.getString("druid.timeBetweenEvictionRunsMillis")));

        // configuration
        Properties properties = new Properties();
        properties.putAll(PropertiesUtil.getProperties());
        datasource.configFromPropety(properties);

        List<Filter> filters = new ArrayList<>();
        filters.add(statFilter());
        filters.add(wallFilter());
//        filters.add(Log4j2Filter());
        datasource.setProxyFilters(filters);

        return datasource;
    }

    private StatFilter statFilter() {
        StatFilter statFilter = new StatFilter();
        statFilter.setLogSlowSql(true);
        statFilter.setMergeSql(true);
        statFilter.setSlowSqlMillis(1000);

        return statFilter;
    }

    private WallFilter wallFilter() {
        WallFilter wallFilter = new WallFilter();

        // 允许执行多条SQL
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);

        //检查查询条件(WHERE/HAVING子句)中是否包含AND永真条件，默认为false，为true表示允许，如果查询时被拦截了再考虑放行
//        config.setConditionAndAlwayTrueAllow(true);
        wallFilter.setConfig(config);

        //观察是否存在违规的情况，但不输出，默认logViolation为false，throwException为true
//        wallFilter.setLogViolation(true);
//        wallFilter.setThrowException(false);

        return wallFilter;
    }

    private Log4j2Filter Log4j2Filter() {
        //打印sql
        Log4j2Filter log4j2Filter = new Log4j2Filter();
        log4j2Filter.setStatementExecutableSqlLogEnable(true);
        log4j2Filter.setStatementSqlPrettyFormat(true);
        return log4j2Filter;
    }

    private boolean isSingle() {
        try {
            return DataUtil.isEmpty(PropertiesUtil.getString("druid.reader.url"));
        } catch (Exception e) {
            return false;
        }
    }
}
